package com.liull.netty.nio.net.selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

import cn.hutool.core.lang.Console;
import com.liull.netty.nio.bytebuffer.ByteBufferUtil;

/**
 * @Author: liull
 * @Description:
 * @Date: Created in 22:19 2021/7/22
 * @Modified By:
 */
public class NonBlockWithSelectorServer {
    public static void main(String[] args) {
        try (ServerSocketChannel ssc = ServerSocketChannel.open()) {
            ssc.configureBlocking(false);
            ssc.bind(new InetSocketAddress(4567));
            // 创建一个Selector
            Selector selector = Selector.open();
            // 每建立一个客户端连接 该channel注册到selector, SelectionKey.OP_ACCEPT表示感兴趣的事件，此时只对Accept事件感兴趣
            final SelectionKey selectionKey = ssc.register(selector, SelectionKey.OP_ACCEPT, null);
            Console.log("register key:{}", selectionKey);
            while (true) {
                // 选择一组键，其对应的通道已准备好进行 I/O 操作，否则线程阻塞
                final int selectCnt = selector.select();
                // 可用的事件集合
                final Set<SelectionKey> selectionKeys = selector.selectedKeys();
                final Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    final SelectionKey key = iterator.next();
                    Console.log("key:{}", key);
                    if (key.isAcceptable()) {
                        final ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                        final SocketChannel socketChannel = channel.accept();
                        socketChannel.configureBlocking(false);
                        ByteBuffer buffer = ByteBuffer.allocate(16);
                        final SelectionKey scKey = socketChannel.register(selector, 0, buffer);
                        Console.log("{}", socketChannel);
                        scKey.interestOps(SelectionKey.OP_READ);
                    } else if (key.isReadable()) {
                        final SocketChannel channel = (SocketChannel) key.channel();
                        final ByteBuffer buffer = (ByteBuffer) key.attachment();
                        try {
                            // -1代表断开
                            final int read = channel.read(buffer);
                            if (read == -1) {
                                key.cancel();
                            } else {
                                split(buffer);
                                if (buffer.position() == buffer.limit()) {
                                    ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity() << 1);
                                    buffer.flip();
                                    newBuffer.put(buffer);
                                    key.attach(newBuffer);
                                }
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                            // 重要 如果不注销的话会一直循环
                            key.cancel();
                        }
                        // 调试信息
                        buffer.flip();
                        ByteBufferUtil.printDesc(buffer);
                    }
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void split(ByteBuffer source) {
        // 切换为读模式
        source.flip();
        for (int i = 0; i < source.limit(); i++) {
            if (source.get(i) == '\n') {
                int length = i + 1 - source.position();
                ByteBuffer target = ByteBuffer.allocate(length);
                // source读 写入target
                for (int j = 0; j < length; j++) {
                    byte b = source.get();
                    target.put(b);
                }
                // 切换为读模式
                target.flip();
                ByteBufferUtil.printDesc(target);

            }
        }
        source.compact();
    }
}
